# Based on: http://groups.google.com/group/pyglet-users/browse_thread/thread/cb1941084576af38/b909c8bcc5a655c9

from OpenGL.GL import * 
from struct import unpack

from common.md2 import MD2NORMALS

# ID value for MD2 model files 
MD2_ID = 844121161
BLUE = 0
YELLOW = 1

class MD2Vertex(object):
	def __init__(self):
		self.vertex = [0.0, 0.0, 0.0]
		self.lightNormal = 0
		
class MD2Frame(object):
	def __init__(self):
		self.scale = [0.0, 0.0, 0.0]
		self.translate = [0.0, 0.0, 0.0]
		self.name = ''
		self.vertices = []
	
class MD2Mesh(object):
	def __init__(self):
		self.pos = [0.0, 0.0, 0.0]
		self.normal = 0
		self.u = 0.0
		self.v = 0.0
		
class MD2Header(object):
	def __init__(self):
		self.id = 0
		self.version = 0
		self.skinWidth = 0
		self.skinHeight = 0
		self.frameSize = 0
		self.numSkins = 0
		self.numVerts = 0
		self.numTexCoords = 0
		self.numTriangles = 0
		self.numGLCmds = 0
		self.numFrames = 0
		self.offsetSkins = 0
		self.offsetTexCoords = 0
		self.offsetTriangles = 0
		self.offsetFrames = 0
		self.offsetGLCmds = 0
		self.offsetEnd = 0
		
class MD2Model(object):
	def __init__(self): 
		object.__init__(self)
		self._fileName = None
		self._numFrames = 0
		self._frameSize = 0
		self._frames = []
		self._numGLCmds = 0 
		self._glCmds = []
		self._numTriangles = 0
		self._lists = 0
		self._textureBlue = None
		self._textureYellow = None
		
	def load(self, fileName, texBlue, texYellow):
		f = open(fileName, 'rb')
		fileData = f.read()
		f.close()
		
		self._textureBlue = texBlue
		self._textureYellow = texYellow
		header = MD2Header()
		
		# Read in the header information
		header.id = (unpack('i', fileData[0:4]))[0]
		if header.id != MD2_ID:
			return False
		
		header.version = (unpack('i', fileData[4:8]))[0]
		if header.version != 8:
			return False
		
		self._fileName = fileName
		
		header.skinWidth = (unpack('i', fileData[8:12]))[0]
		header.skinHeight = (unpack('i', fileData[12:16]))[0]
		header.frameSize = (unpack('i', fileData[16:20]))[0]
		header.numSkins = (unpack('i', fileData[20:24]))[0]
		header.numVerts = (unpack('i', fileData[24:28]))[0]
		header.numTexCoords = (unpack('i', fileData[28:32]))[0]
		header.numTriangles = (unpack('i', fileData[32:36]))[0]
		header.numGLCmds = (unpack('i', fileData[36:40]))[0]
		header.numFrames = (unpack('i', fileData[40:44]))[0]
		header.offsetSkins = (unpack('i', fileData[44:48]))[0]
		header.offsetTexCoords = (unpack('i', fileData[48:52]))[0]
		header.offsetTriangles = (unpack('i', fileData[52:56]))[0]
		header.offsetFrames = (unpack('i', fileData[56:60]))[0]
		header.offsetGLCmds = (unpack('i', fileData[60:64]))[0]
		header.offsetEnd = (unpack('i', fileData[64:68]))[0] 
		
		# Read in the frame data
		for i in range(header.numFrames):
			frameInfo = fileData[header.offsetFrames + (i * header.frameSize):header.offsetFrames + ((i + 1) * header.frameSize)]
			
			frame = MD2Frame()
			
			frame.scale[0] = (unpack('f', frameInfo[0:4]))[0]
			frame.scale[1] = (unpack('f', frameInfo[4:8]))[0]
			frame.scale[2] = (unpack('f', frameInfo[8:12]))[0]
			
			frame.translate[0] = (unpack('f', frameInfo[12:16]))[0]
			frame.translate[1] = (unpack('f', frameInfo[16:20]))[0]
			frame.translate[2] = (unpack('f', frameInfo[20:24]))[0]
			
			name = unpack('16B', frameInfo[24:40])
			for c in range(16):
				frame.name = frame.name + chr(name[c])
				
			for j in range(40, len(frameInfo), 4):
				vertex = MD2Vertex()
				vertex.vertex[0] = (unpack('B', frameInfo[j]))[0]
				vertex.vertex[1] = (unpack('B', frameInfo[j + 1]))[0]
				vertex.vertex[2] = (unpack('B', frameInfo[j + 2]))[0]
				
				vertex.lightNormal = (unpack('B', frameInfo[j + 3]))[0] 
				frame.vertices.append(vertex)
				
			self._frames.append(frame)
			
		cmdInfo = fileData[header.offsetGLCmds:header.offsetGLCmds + (header.numGLCmds * 4)]
		
		i = 0
		# Read in the vertex data
		while i < len(cmdInfo):
			# Number of vertices
			numVerts = (unpack('l', cmdInfo[i:i + 4]))[0]
			self._glCmds.append(numVerts)
			i += 4
			if numVerts < 0:
				numVerts = -numVerts
			# Texture coordinates
			for j in range(numVerts):
				self._glCmds.append((unpack('f', cmdInfo[i:i + 4]))[0])
				self._glCmds.append((unpack('f', cmdInfo[i + 4:i + 8]))[0])
				# Vertex index
				self._glCmds.append((unpack('l', cmdInfo[i + 8:i + 12]))[0])
				i += 12
				
		self._numFrames = header.numFrames
		self._numGLCmds = header.numGLCmds
		self._frameSize = header.frameSize
		self._numTriangles = header.numTriangles
		
		self._lists = glGenLists(self._numFrames * 2)
		
		# Compile the model vertex data into display lists
		for i in range(self._numFrames):
			glNewList(self._lists + i, GL_COMPILE)
			self.compile(i, BLUE)
			glEndList()
			
		for i in range(self._numFrames):
			glNewList(self._lists + self._numFrames + i, GL_COMPILE)
			self.compile(i, YELLOW)
			glEndList()
			
		return True
	
	def compile(self, frame, color): 
		"""
		Compiles a frame's data into a display list.
		""" 
		
		glCmd = 0
		while self._glCmds[glCmd] != 0:
			if self._glCmds[glCmd] > 0:
				numVert = self._glCmds[glCmd]
				type = 0
			else:
				numVert = -self._glCmds[glCmd]
				type = 1
			glCmd += 1
			
			if numVert < 0:
				numVert = -numVert
				
			if color == BLUE:
				glBindTexture(GL_TEXTURE_2D, self._textureBlue)
			elif color == YELLOW:
				glBindTexture(GL_TEXTURE_2D, self._textureYellow)
			
			if type == 0:
				glBegin(GL_TRIANGLE_STRIP)
			else:
				glBegin(GL_TRIANGLE_FAN)
				
			for i in range(numVert):
				mesh = MD2Mesh()
				mesh.u = self._glCmds[glCmd]
				glCmd += 1
				mesh.v = self._glCmds[glCmd]
				glCmd += 1 

				vertIndex = self._glCmds[glCmd]
				glCmd += 1
				mesh.pos[0] = (self._frames[frame].vertices[vertIndex].vertex[0] * self._frames[frame].scale[0]) + self._frames[frame].translate[0]
				mesh.pos[1] = (self._frames[frame].vertices[vertIndex].vertex[1] * self._frames[frame].scale[1]) + self._frames[frame].translate[1]
				mesh.pos[2] = (self._frames[frame].vertices[vertIndex].vertex[2] * self._frames[frame].scale[2]) + self._frames[frame].translate[2]
				mesh.normal = self._frames[frame].vertices[vertIndex].lightNormal
				
				glTexCoord2f(mesh.u, mesh.v)
				glNormal3f(*MD2NORMALS[mesh.normal])
				glVertex3f(*mesh.pos)
					
			glEnd()
				
	def render(self, frame, color):
		offset = 0
		if color == YELLOW:
			offset += self._numFrames
		
		glCallList(self._lists + frame + offset)